package com.duobaoyu.dby.supply.biz.util;

import com.duobaoyu.dby.supply.common.biz.compare.Compare;
import com.duobaoyu.dby.supply.common.biz.compare.CompareResult;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 使用须知： <br>
 * （1）该工具类主要用于两个同类对象的属性值比较； <br>
 * （2）使用本工具类前，请将对应的类属性上打上 @Compare("xxx") 注解，其中xxx为字段的表达名称；<br>
 * （3）为了比较灵活，只有打了该注解才会进行比较，不打的字段则不会进行比较 <br>
 * （4）比较后，只会返回有变化的字段，无变化的字符则不返回 <br>
 */
@Slf4j
@UtilityClass
public class CompareUtils {


	/**
	 * 属性比较
	 *
	 * @param source 源数据对象
	 * @param target 目标数据对象
	 * @return 对应属性值的比较变化
	 */
	public <T> CompareResult compare(T source, T target) {
		return compare(source, target, null);
	}


	/**
	 * 属性比较
	 *
	 * @param source 源数据对象
	 * @param target 目标数据对象
	 * @param ignoreCompareFields 忽略比较的字段
	 * @return 对应属性值的比较变化
	 */
	public <T> CompareResult compare(T source, T target, List<String> ignoreCompareFields) {
		if (Objects.isNull(source) && Objects.isNull(target)) {
			return CompareResult.builder().build();
		}
		Map<String, CompareNode> sourceMap = getFiledValueMap(source);
		Map<String, CompareNode> targetMap = getFiledValueMap(target);
		if (sourceMap.isEmpty() && targetMap.isEmpty()) {
			return CompareResult.builder().build();
		}
		// 如果源数据为空，则只显示目标数据，不显示属性变化情况
		if (sourceMap.isEmpty()) {
			return doEmpty(targetMap, ignoreCompareFields);
		}
		// 如果源数据为空，则显示属性变化情况
		return doCompare(sourceMap, targetMap, ignoreCompareFields);
	}

	private CompareResult doEmpty(Map<String, CompareNode> targetMap, List<String> ignoreCompareFields) {
		ArrayList<CompareResult.Diff> diffs = new ArrayList<>(targetMap.size());
		for (CompareNode node : targetMap.values()) {

			if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(node.getFieldKey())) {
				continue;
			}
			diffs.add(CompareResult.Diff.builder().fieldName(node.getFieldName()).fieldKey(node.getFieldKey()).newValue(node.getFieldValue()).build());
		}
		return CompareResult.builder().diffList(diffs).build();
	}

	private CompareResult doCompare(Map<String, CompareNode> sourceMap, Map<String, CompareNode> targetMap, List<String> ignoreCompareFields) {
		ArrayList<CompareResult.Diff> diffs = new ArrayList<>(targetMap.size());
		for (Map.Entry<String, CompareNode> stringCompareNodeEntry : sourceMap.entrySet()) {
			String key = stringCompareNodeEntry.getKey();
			CompareNode sn = stringCompareNodeEntry.getValue();
			CompareNode tn = targetMap.get(key);
			if (Objects.nonNull(ignoreCompareFields) && ignoreCompareFields.contains(sn.getFieldKey())) {
				continue;
			}
			// 只有两者属性值不一致时, 才显示变化情况
			if (!Objects.equals(sn.getFieldValue(), tn.getFieldValue())) {
				diffs.add(CompareResult.Diff.builder().fieldName(sn.getFieldName()).fieldKey(sn.getFieldKey()).newValue(tn.getFieldValue()).oldValue(sn.getFieldValue()).build());
			}
		}
		return CompareResult.builder().diffList(diffs).build();
	}

	private <T> Map<String, CompareNode> getFiledValueMap(T t) {
		if (Objects.isNull(t)) {
			return Collections.emptyMap();
		}
		Field[] fields = t.getClass().getDeclaredFields();
		if (Objects.isNull(fields) || fields.length == 0) {
			return Collections.emptyMap();
		}
		Map<String, CompareNode> map = new LinkedHashMap();
		for (Field field : fields) {
			Compare compareAnnotation = field.getAnnotation(Compare.class);
			if (Objects.isNull(compareAnnotation)) {
				continue;
			}
			field.setAccessible(true);
			try {
				String fieldKey = field.getName();
				CompareNode node = new CompareNode();
				node.setFieldKey(fieldKey);
				node.setFieldValue(field.get(t));
				node.setFieldName(compareAnnotation.value());
				map.put(field.getName(), node);
			} catch (IllegalArgumentException | IllegalAccessException e) {
				log.warn("CompareUtils#getFiledValueMap error", e);
			}
		}
		return map;
	}


}
